package com.xuecheng.content.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.api.content.model.dto.CourseBaseDTO;
import com.xuecheng.api.content.model.dto.TeachplanDTO;
import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.enums.common.CommonEnum;
import com.xuecheng.common.enums.content.CourseAuditEnum;
import com.xuecheng.common.enums.content.CourseModeEnum;
import com.xuecheng.common.enums.content.TeachPlanEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.StringUtil;
import com.xuecheng.content.common.constant.ContentErrorCode;
import com.xuecheng.content.common.util.QiniuUtils;
import com.xuecheng.content.convert.CourseBaseConvert;
import com.xuecheng.content.convert.TeachplanConvert;
import com.xuecheng.content.entity.CourseBase;
import com.xuecheng.content.entity.Teachplan;
import com.xuecheng.content.entity.TeachplanMedia;
import com.xuecheng.content.entity.ext.TeachlpanNode;
import com.xuecheng.content.mapper.TeachplanMapper;
import com.xuecheng.content.service.CourseBaseService;
import com.xuecheng.content.service.TeachplanMediaService;
import com.xuecheng.content.service.TeachplanService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 课程计划 服务实现类
 * </p>
 */

@Service
public class TeachplanServiceImpl extends ServiceImpl<TeachplanMapper, Teachplan> implements TeachplanService {

    //课程
    @Autowired
    private CourseBaseService courseBaseService;

    //课程计划
    @Autowired
    private TeachplanService teachplanService;

    //课程相关媒资
    @Autowired
    private TeachplanMediaService teachplanMediaService;

    @Autowired
    private TeachplanMapper teachplanMapper;

    /*
     * 业务分析：
     *   1.判断关键数据
     *       courseId companyId
     *   2.判断业务数据
     *       课程基础信息
     *           判断是否存在
     *           判断是否是同一家教学机构
     *           判断是否删除
     *   3.根据Courseid查询课程计划数据
     *   4.通过java的递归生成课程计划树形结构
     *   5.将数据转为dto并返回
     * */

    public TeachplanDTO queryTreeNodesByCourseId(Long courseId, Long companyId) {
        //1.判断关键数据
        if (ObjectUtils.isEmpty(courseId) || ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }

        //  2.判断业务数据
        //      课程基础信息
        //          判断是否存在
        //          判断是否是同一家教学机构
        //          判断是否删除
        LambdaQueryWrapper<CourseBase> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CourseBase::getId, courseId);
        wrapper.eq(CourseBase::getCompanyId, companyId);
        CourseBase courseBase = courseBaseService.getOne(wrapper);
        if (ObjectUtils.isEmpty(courseBase)) {
            ExceptionCast.cast(ContentErrorCode.E_120013);
        }
        Integer status = courseBase.getStatus();
        if (!(CommonEnum.USING_FLAG.getCodeInt().equals(status))) {
            ExceptionCast.cast(ContentErrorCode.E_120015);
        }

        //  3.根据CourseId查询课程计划数据
        TeachplanMapper baseMapper = this.getBaseMapper();
        List<TeachlpanNode> nodes = baseMapper.selectNodesByCourseId(courseId);

        //  4.通过java的递归生成课程计划树形结构
        TeachplanDTO teachplanDTO;
        if (CollectionUtils.isEmpty(nodes)) {
            teachplanDTO = new TeachplanDTO();
        } else {
            TeachlpanNode rootNode = nodes.remove(0);
            createTreeNodes(rootNode, nodes);
            teachplanDTO = TeachplanConvert.INSTANCE.node2dto(rootNode);
        }
        return teachplanDTO;

    }

    private void createTreeNodes(TeachlpanNode rootNode, List<TeachlpanNode> teachplanNodes) {
        //判断父级的集合是否为空，如果为空的话就要创建出一个集合对象来
        if (ObjectUtils.isEmpty(rootNode.getChildrenNodes())) {
            rootNode.setChildrenNodes(new ArrayList<>());
        }

        //循环nodes集合数据
        for (TeachlpanNode teachplanNode : teachplanNodes) {
            if (teachplanNode.getParentid().equals(rootNode.getId())) {
                rootNode.getChildrenNodes().add(teachplanNode);
                if (!(teachplanNode.getGrade().equals(TeachPlanEnum.THIRD_LEVEL))) {
                    createTreeNodes(teachplanNode, teachplanNodes);
                }
            }
        }
    }

    /**
     * 业务操作主方法
     * 新增或修改课程计划
     * 1.判断业务操作
     * 判断dto是否有teachplanid
     * 如果有->修改
     * 如果没有->新增
     * 2.业务操作后要返回结果内容
     * 3.将结果内容返回
     *
     * @param dto
     * @return
     */
    @Override
    public TeachplanDTO createOrModifyTeachplan(TeachplanDTO dto, Long companyId) {
        //1.判断业务操作
        //     判断dto是否有teachplanid
        //         如果有->修改
        //         如果没有->新增
        // 2.业务操作后要返回结果内容DTO
        Long teachPlanId = dto.getTeachPlanId();

        TeachplanDTO resultDTO = null;

        if (ObjectUtils.isEmpty(teachPlanId)) {
            //新增课堂计划
            resultDTO = createTeachplan(dto, companyId);
        } else {
            //修改课程计划
            resultDTO = mofifyTeachplan(dto, companyId);
        }
        return resultDTO;
    }

    /**
     * 删除课程计划
     *
     * @param teachPlanId {@link Long} 课程计划Id
     * @param companyId   {@link Long} 公司Id
     */
    public void removeTeachPlan(Long teachPlanId, Long companyId) {
        //判断前端传来的数据是否为空
        if (ObjectUtils.isEmpty(teachPlanId) || ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(ContentErrorCode.E_120401);
        }
        //根据前端传来的id获得计划课程对象
        Teachplan teachplan = teachplanService.getById(teachPlanId);

        //根据前端传来的id获得课程对象
        CourseBase courseBase = courseBaseService.getById(teachplan.getCourseId());

        //判断是否删除
        if (CommonEnum.DELETE_FLAG.equals(teachplan.getStatus())) {
            ExceptionCast.cast(ContentErrorCode.E_120412);
        }

        //判断审核状态(未提交和审核未通过)
        if (CourseAuditEnum.AUDIT_UNLINE_STATUS.equals(courseBase.getAuditStatus()) ||
                CourseAuditEnum.AUDIT_DISPAST_STATUS.equals(courseBase.getAuditStatus())) {
            ExceptionCast.cast(ContentErrorCode.E_120011);
        }

     /*   //判断课程计划是不是二级数据
        if (!(TeachPlanEnum.SECEND_LEVEL.equals(teachplanServiceById.getGrade()))) {
            LambdaQueryWrapper<Teachplan> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(Teachplan::getParentid, teachPlanId);
            int count = this.count(wrapper);
            if (count > 0) {
                ExceptionCast.cast(ContentErrorCode.E_120414);
            }
        }
*/

        //创建课程媒资对象
        TeachplanMedia teachplanMedia = teachplanMediaService.getById(teachPlanId);
        //判断是不是三级数据
//        if (TeachPlanEnum.THIRD_LEVEL.equals(teachplanServiceById.getGrade())) {
//            //三级计划课程下是否是录播课程
//            //  如果是录播是否关联数据
//            if (courseBase.getTeachmode().equals(CourseModeEnum.COURSE_MODE_RECORD_STATUS)) {
//                if (teachplanMedia.getTeachplanId().equals(teachPlanId)) {
//                    ExceptionCast.cast(ContentErrorCode.E_120409);
//                }
//            }
//        } else {
//            ExceptionCast.cast(ContentErrorCode.E_120414);
//        }
        if (TeachPlanEnum.SECEND_LEVEL.equals(teachplan.getGrade())) {
            LambdaQueryWrapper<Teachplan> teachplanLambdaQueryWrapper = new LambdaQueryWrapper<>();
            teachplanLambdaQueryWrapper.eq(Teachplan::getParentid, teachPlanId);

            int count = this.count(teachplanLambdaQueryWrapper);
            if (count > 0) {
                ExceptionCast.cast(ContentErrorCode.E_120409);
            }
        }


        LambdaQueryWrapper<TeachplanMedia> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TeachplanMedia::getTeachplanId,teachPlanId);
        int count = teachplanMediaService.count(queryWrapper);
        if (count > 0) {
            ExceptionCast.cast(ContentErrorCode.E_120409);
        }

        //删除课程计划id
        boolean removeTeachplan = teachplanService.removeById(teachPlanId);

        //判断删除是否成功
        if (!removeTeachplan) {
            ExceptionCast.cast(ContentErrorCode.E_120407);
        }
    }


    /**
     * 课程的业务和关键数据的判断
     *
     * @param dto
     * @param companyId
     * @return
     */
    private TeachplanDTO mofifyTeachplan(TeachplanDTO dto, Long companyId) {
        //判断关键数据和业务数据
        //  CourseBaseDTO courseBase = getCourseBaseDTO(dto, companyId);

        // 1.判断关键数据
        // 2.判断业务数据
        //      课程基础信息
        //          判断是否存在
        //          判断是否同一家机构
        //          判断是否删除
        //          判断课程基础信息审核状态：未提交和审核未通过

        //判断前端传来的公司ID是否为空
        if (ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(ContentErrorCode.E_120009);
        }


        //根据课程计划对象的课程id获得课程PO对象
        CourseBase courseBase = courseBaseService.getById(dto.getCourseId());

        //2.  判断是否存在
        // 判断是否删除
        // 判断审核状态：未提交和审核未通过
        if (CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(courseBase.getAuditStatus()) ||
                CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(courseBase.getAuditStatus()) ||
                CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(courseBase.getAuditStatus())) {
            ExceptionCast.cast(ContentErrorCode.E_120015);
        }


        //获取当前课程的课程计划
        Teachplan teachplan = teachplanService.getById(dto.getTeachPlanId());

        //将po转化为dto
        TeachplanDTO teachplanDTO = TeachplanConvert.INSTANCE.entity2dto(teachplan);

        //判断是否是一家机构
        if (!(courseBase.getCompanyId().equals(companyId))) {
            ExceptionCast.cast(ContentErrorCode.E_120018);
        }

        //判断课程计划
        if (ObjectUtils.isEmpty(teachplanDTO.getTeachPlanId())) {
            ExceptionCast.cast(ContentErrorCode.E_120402);
        }
        //判断课程计划的等级
        if (teachplanDTO.getGrade().equals(TeachPlanEnum.FIRST_LEVEL) ||
                teachplanDTO.getGrade().equals(TeachPlanEnum.FIRST_PARENTID_FLAG)) {
            ExceptionCast.cast(ContentErrorCode.E_120407);
        }

        //修改课程计划数据
        LambdaUpdateWrapper<Teachplan> updateWrapper = new LambdaUpdateWrapper<>();
        //赋值
        updateWrapper.set(Teachplan::getPname, teachplanDTO.getPname());
        updateWrapper.set(Teachplan::getChangeDate, LocalDateTime.now());
        updateWrapper.set(Teachplan::getIsPreview, teachplanDTO.getIsPreview());

        if (courseBase.getTeachmode().equals(CourseModeEnum.COURSE_MODE_LIVE_STATUS)) {
            updateWrapper.set(Teachplan::getStartTime, teachplanDTO.getStartTime());
            updateWrapper.set(Teachplan::getEndTime, teachplanDTO.getEndTime());
        }

        //手动添加,因为LambdaUpdateWrapper会破坏mp自动填充
        updateWrapper.set(Teachplan::getChangeDate, LocalDateTime.now());

        //修改
        boolean result = this.update(updateWrapper);
        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120407);
        }

        //获取课程大纲信息
        Teachplan teachplanPo = this.getById(dto.getTeachPlanId());
        TeachplanDTO entity2dto = TeachplanConvert.INSTANCE.entity2dto(teachplanPo);
        return entity2dto;
    }

    //课程计划的新增
    /* 课程计划的新增
     *  业务分析：
     *   1.判断关键数据
     *       pname  courseId  companyId
     *   2.判断业务数据
     *       课程基础信息
     *           判断是否存在
     *           判断是否删除
     *           判断是否是同一家机构
     *           判断审核状态：未提交和审核未通过
     *   3.给数据赋值：grade、parentId
     *       获得新增数据的父级数据
     *          通过父级数据：grade+1=当前数据的级别
     *          通过父级数据：parentId
     *          通过父级数据：给添加数据orderby赋值
     *               bug版：统计父级数据子级的个数+1
     *       关联数据：父级id值
     *   4.将dto数据转为po并保存到数据中
     *   5.判断操作的结果并返回数据dto
     *  */
    private TeachplanDTO createTeachplan(TeachplanDTO dto, Long companyId) {
        //CourseBaseDTO courseBase = getCourseBaseDTO(dto, companyId);

        //1.判断pname  courseId  companyId
        if (ObjectUtils.isEmpty(dto.getPname()) ||
                ObjectUtils.isEmpty(dto.getCourseId()) ||
                ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(ContentErrorCode.E_120010);
        }

        //根据课程计划获取课程对象
        CourseBase courseBase = courseBaseService.getById(dto.getCourseId());

        //将po状态改为dto
        CourseBaseDTO courseBaseDTO = CourseBaseConvert.INSTANCE.entity2dto(courseBase);


        //2.  判断是否存在
        // 判断是否删除
        // 判断是否是同一家机构
        // 判断审核状态：未提交和审核未通过
        if (CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(courseBaseDTO.getAuditStatus()) ||
                CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(courseBaseDTO.getAuditStatus()) ||
                CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(courseBaseDTO.getAuditStatus())) {
            ExceptionCast.cast(ContentErrorCode.E_120015);
        }

        // 3.给数据赋值：grade、parentId
        //     获得新增数据的父级数据
        Teachplan parentNode = getParentDate(dto, courseBaseDTO);


        //        通过父级数据：grade+1=当前数据的级别
        dto.setGrade(parentNode.getGrade() + 1);

        //通过父级数据：parentId
        dto.setParentid(parentNode.getId());

        //        通过父级数据：给添加数据orderby赋值
        //             bug版：统计父级数据子级的个数+1
        // select count(1) from teachplan where parentId = 父级id值
        LambdaUpdateWrapper<Teachplan> queryWrapper = new LambdaUpdateWrapper<>();

        if (!ObjectUtils.isEmpty(parentNode.getId())) {
            queryWrapper.set(Teachplan::getParentid, parentNode.getId());
        } else {
            queryWrapper.set(Teachplan::getParentid, TeachPlanEnum.FIRST_PARENTID_FLAG);
        }

        int count = this.count(queryWrapper);
        dto.setOrderby(count + 1);
        //将课程计划dto改为po
        Teachplan teachplan = TeachplanConvert.INSTANCE.dto2entity(dto);

        boolean result = this.save(teachplan);
        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120407);
        }
        //4.判断操作的结果并返回数据dto
        Teachplan po = this.getById(teachplan.getId());
        TeachplanDTO resultDTO = TeachplanConvert.INSTANCE.entity2dto(po);
        return resultDTO;
    }

    /**
     * 判断课程计划的业务数据和审核状态
     *
     * @param dto
     * @param companyId
     * @return
     */
    private CourseBaseDTO getCourseBaseDTO(TeachplanDTO dto, Long companyId) {
        //1.判断相关数据 pname courseId companyId
        if (StringUtil.isBlank(dto.getPname()) ||
                ObjectUtils.isEmpty(dto.getCourseId()) ||
                ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }

        // 2.判断业务数据
        //     课程基础信息
        //         判断是否存在
        //         判断是否删除
        //         判断是否是同一家机构
        //         判断审核状态：未提交和审核未通过

        CourseBaseDTO courseBase = courseBaseService.getByCourseId(dto.getCourseId(), companyId);
        //获取当前课程对象的审核状态
        String auditStatus = courseBase.getAuditStatus();
        if (CourseAuditEnum.AUDIT_UNLINE_STATUS.equals(auditStatus) ||
                CourseAuditEnum.AUDIT_DISPAST_STATUS.equals(auditStatus) ||
                CourseAuditEnum.AUDIT_PUBLISHED_STATUS.equals(auditStatus)) {
            ExceptionCast.cast(ContentErrorCode.E_120015);
        }
        return courseBase;
    }


    /*
        获得二级和三级课程计划父级数据（一级和二级）
        1.课程计划的一级（根级）
        - 数据来源：课程基础信息（CourseBase）
        - 创建时机：在第一次添加二级数据时自动生成
        - 注意事项：一级父级（根级）父级 ID 值为 0

        2.课程计划的二级和三级节点
        - 数据来源：教育机构人员在页面填写的数据
        - 创建时机：教育机构人员提交课程计划数据
        - 注意事项：添加时要校验父级菜单是否存在
          - 二级在添加时
            - 父级不存在（一级）：自动创建并获得获得父级Id值
            - 父级存在（一级）：获得父级Id值
          - 三级在添加时
            - 父级不存在（二级）：抛出异常
            - 父级存在（二级）：获得父级Id值
    * */
    private Teachplan getParentDate(TeachplanDTO dto, CourseBaseDTO courseBase) {
        // 判断新增数据是否是三级还是二级
        // 根据parentid来判断
        //   如果有：三级课程计划   -->父级（二级）
        //   如果没有：二级级课程计划 -->父级（一级）
        Long parentid = dto.getParentid();
        if (ObjectUtils.isEmpty(parentid)) {
            // 获得二级父级数据
            // 根据courseId和parentid=0来查找一个课程下一级课程计划数据

            //构建条件构造器
            LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
            //判断课程计划表里是否有该课程id
            queryWrapper.eq(Teachplan::getCourseId, courseBase.getCourseBaseId());

            queryWrapper.eq(Teachplan::getParentid, TeachPlanEnum.FIRST_PARENTID_FLAG);

            Teachplan parentNode = this.getOne(queryWrapper);
            if (ObjectUtils.isEmpty(parentNode)) {
                //在后端创建课程计划一级数据
                parentNode = new Teachplan();
                parentNode.setPname(courseBase.getName());
                parentNode.setParentid(new Long(TeachPlanEnum.FIRST_PARENTID_FLAG));
                parentNode.setGrade(TeachPlanEnum.FIRST_LEVEL);
                parentNode.setOrderby(TeachPlanEnum.FIRST_LEVEL);
                parentNode.setCourseId(courseBase.getCourseBaseId());
                boolean result = this.save(parentNode);
                if (!result) {
                    ExceptionCast.cast(ContentErrorCode.E_120415);
                }
            }
            return parentNode;
        } else {
            // 获得三级父级数据
            LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Teachplan::getId, parentid);
            queryWrapper.eq(Teachplan::getCourseId, courseBase.getCourseBaseId());
            Teachplan parentNode = this.getOne(queryWrapper);
            if (ObjectUtils.isEmpty(parentNode)) {
                ExceptionCast.cast(ContentErrorCode.E_120408);
            }
            return parentNode;
        }
    }
}
